1+
2+3;
6
Rendered with Quarto
GAP
GAP
is a discrete algebraic computing environment (GAP
stands for Groups, Algorithms, and Programming). It has a core implemented in c
and has separate libraries written in its own programming language.
This language is procedural, so people who have programmed in pascal
, c
, maple
, … have no problems in adapting. On the official GAP
website you can find a lot of information concerning installation on different platforms, tutorials, manuals, and packages.
GAP
packages go through a refereeing process similar to that of scientific journals. They are first deposited (and publicly accessible on the official website) and if accepted they become part of the distribution.
GAP
has been maintained throughout its history by several universities, and is supported by contributions from users in the form of packages for more specific purposes.
GAP
is an interpreted language (although it has a compiler). Operations can be performed directly on the command line, or scripted files written in a text editor can be read.
GAP
is normally invoked using the gap
command, although depending on the operating system there may be different alternatives. Once the interpreter is started, a command line appears that looks like this.
To edit the entries you want to evaluate, you can use the arrow keys together with the start and end keys. There is also the possibility of using key combinations similar to those used in a unix shell (ctr-[b,f,p,n,a,e]
). The tab key is used to complete commands from the text you have entered at that moment. Thus typing Gcd+TAB
results in:
gap> Gcd
Gcd
GcdCoeffs
GcdInt
GcdOp
GcdRepresentation
GcdRepresentationOp
Gcdex
To exit the command line, you can use the command quit;
, or press ctr-d
.
The end of a line is not indicated by a carriage return, but by ;
(to evaluate, press enter
). This allows you to write multi-line statements.
On jupyter
to evaluate you have to press shift+enter
.
The working session can be stored in a file for later editing using the LogTo
command. With LogTo("tests/log");
we save to the log
file in the tests
folder. With LogTo();
we interrupt the recording.
We can read a script with the Read
command (in this example one.g
contains only a:=1;
).
Note that the slashes are in the form /
, even on windows.
Help is accessed using a question mark. For example, ?LogTo
shows help for that command.
The operations of addition and product depend on the arguments that accompany them. They can represent the sum of integers, or of matrices, or of vector subspaces… The product can even mean the composition of two permutations, and the power (^
) the image of a point by a permutation.
The symbols =
, <
, >
, <=
, >=
, and <>
are used to denote equality, less than, greater than, less than or equal, greater than or equal, and different, respectively sirven para denotar igualdad, ser mayor, menor, menor o igual, mayor o igual y distinto, respectivamente.
If we are going to use an object several times, we can assign it a name for convenience.
If we want to inhibit the output, we write an extra semicolon at the end of the assignment.
We can visualise the variables defined so far as follows.
[ "AcceptNewConnection", "AttachServingSocket", "BGJobByForkType", "BackgroundJobByFork", "BackgroundJobByForkChild", "BackgroundJobByForkOptions", "BackgroundJobsFamily", "CRYPTING_HexStringIntPad", "CRYPTING_HexStringIntPad8", "CRYPTING_SHA256_FINAL", "CRYPTING_SHA256_HMAC", "CRYPTING_SHA256_INIT", "CRYPTING_SHA256_State_Family", "CRYPTING_SHA256_State_Type", "CRYPTING_SHA256_UPDATE", "ChangeDirectoryCurrent", "CheckForUpdates", "CloseConnection", "CloseHTTPConnection", "CompareTimes", "DifferenceTimes", "DoIO", "DoQueues", "FileFamily", "FileType", "FixChunkedBody", "GAPJupyterKernelType", "GET_HELP_URL", "GapToJsonStream", "GapToJsonString", "GetInput", "GetLenFrom8Bytes", "HMACSHA256", "HPCGAPJupyterKernelType", "HTTPRequest", "HTTPTimeoutForSelect", "HasJupyterRenderableData", "HasJupyterRenderableMetadata", "HasProcessID", "HasTerminated", "HexStringUUID", "IO", "IOHub", "IOHubFamily", "IOHubType", "IO_AddToPickled", "IO_AddToUnpickled", "IO_ClearPickleCache", "IO_Close", "IO_CloseAllFDs", "IO_CompressedFile", "IO_EOF", "IO_Environment", "IO_Error", "IO_File", "IO_FileFilterString", "IO_FilteredFile", "IO_FinalizePickled", "IO_FinalizeUnpickled", "IO_FindExecutable", "IO_Flush", "IO_FlushNonBlocking", "IO_ForkExecWithFDs", "IO_GenericObjectPickler", "IO_GenericObjectUnpickler", "IO_GetFD", "IO_GetWBuf", "IO_HasData", "IO_IgnorePid", "IO_InstallSIGCHLDHandler", "IO_IsAlreadyPickled", "IO_ListDir", "IO_MakeEnvList", "IO_MakeIPAddressPort", "IO_Nothing", "IO_OK", "IO_PICKLECACHE", "IO_PackageIsLoaded", "IO_Pickle", "IO_PickleByString", "IO_PipeThrough", "IO_PipeThroughWithError", "IO_Popen", "IO_Popen2", "IO_Popen3", "IO_Read", "IO_ReadAttribute", "IO_ReadBlock", "IO_ReadLine", "IO_ReadLines", "IO_ReadSmallInt", "IO_ReadUntilEOF", "IO_ReadyForFlush", "IO_ReadyForWrite", "IO_RestoreSIGCHLDHandler", "IO_Result", "IO_ResultsFamily", "IO_Select", "IO_SendStringBackground", "IO_StartPipeline", "IO_StringFilterFile", "IO_Unpickle", "IO_UnpickleByEvalString", "IO_UnpickleByFunction", "IO_Unpicklers", "IO_WaitPid", "IO_WrapFD", "IO_Write", "IO_WriteAttribute", "IO_WriteFlush", "IO_WriteLine", "IO_WriteLines", "IO_WriteNonBlocking", "IO_WriteSmallInt", "IO_accept", "IO_bind", "IO_chdir", "IO_chmod", "IO_chown", "IO_close", "IO_closedir", "IO_connect", "IO_creat", "IO_dup", "IO_dup2", "IO_environ", "IO_execv", "IO_execve", "IO_execvp", "IO_exit", "IO_fchmod", "IO_fchown", "IO_fcntl", "IO_fork", "IO_fstat", "IO_getcwd", "IO_gethostbyname", "IO_gethostname", "IO_getpid", "IO_getppid", "IO_getsockname", "IO_getsockopt", "IO_gettimeofday", "IO_gmtime", "IO_kill", "IO_lchown", "IO_link", "IO_listen", "IO_localtime", "IO_lseek", "IO_lstat", "IO_make_sockaddr_in", "IO_mkdir", "IO_mkdtemp", "IO_mkfifo", "IO_mknod", "IO_mkstemp", "IO_open", "IO_opendir", "IO_pipe", "IO_read", "IO_readdir", "IO_readlink", "IO_recv", "IO_recvfrom", "IO_rename", "IO_rewinddir", "IO_rmdir", "IO_seekdir", "IO_select", "IO_send", "IO_sendto", "IO_setsockopt", "IO_socket", "IO_stat", "IO_symlink", "IO_telldir", "IO_unlink", "IO_write", "ISO8601Stamp", "InfoIO", "InputQueue", "IsBackgroundJob", "IsBackgroundJobByFork", "IsFile", "IsGAPJupyterKernel", "IsHPCGAPJupyterKernel", "IsIOHub", "IsIOHubCat", "IsIdle", "IsJupyterKernel", "IsJupyterRenderable", "IsJupyterRenderableRep", "IsOutputStreamZmqRep", "IsRealRandomSource", "IsSHA256State", "IsUUID", "IsUUIDBlistRep", "IsWorkerFarm", "IsWorkerFarmByFork", "IsZmqSocket", "JSON_ESCAPE_STRING", "JSON_STREAM_TO_GAP", "JSON_STRING_TO_GAP", "JUPYTER_Complete", "JUPYTER_FindHelp", "JUPYTER_FindManSection", "JUPYTER_FormatKnown", "JUPYTER_HELP", "JUPYTER_HELP_SHOW_MATCHES", "JUPYTER_Inspect", "JUPYTER_KERNEL_MODE_CONTROL", "JUPYTER_KERNEL_MODE_EXEC", "JUPYTER_KernelLoop", "JUPYTER_KernelStart_GAP", "JUPYTER_KernelStart_HPC", "JUPYTER_LogProtocol", "JUPYTER_UnlogProtocol", "JUPYTER_print", "JsonStreamToGap", "JsonStringToGap", "JupyterDefaultKernelConfig", "JupyterMsg", "JupyterMsgDecode", "JupyterMsgEncode", "JupyterMsgRecv", "JupyterMsgSend", "JupyterRender", "JupyterRenderable", "JupyterRenderableData", "JupyterRenderableMetadata", "JupyterRenderableType", "JupyterSplashDot", "JupyterSplashSubgroupLattice", "JupyterSplashTikZ", "Kill", "LastReadValue", "NewConnection", "NewJupyterKernel", "NewTCPConnection", "NewUUID", "OpenHTTPConnection", "OutputQueue", "OutputStreamZmq", "OutputStreamZmqType", "ParDoByFork", "ParDoByForkOptions", "ParListByFork", "ParListByForkOptions", "ParListWorker", "ParMapReduceByFork", "ParMapReduceByForkOptions", "ParMapReduceWorker", "ParTakeFirstResultByFork", "ParTakeFirstResultByForkOptions", "ParWorkerFarmByFork", "ParWorkerFarmByForkOptions", "Pickup", "ProcessID", "RandomUUID", "ReadWeb", "Run", "SHA256String", "SetJupyterRenderableData", "SetJupyterRenderableMetadata", "SetProcessID", "Shutdown", "ShutdownServingSocket", "SingleHTTPRequest", "StoreLenIn8Bytes", "StringUUID", "Submit", "SubmitOutput", "SynchronizationFamily", "TYPE_ZMQ_SOCKET", "UUIDFamily", "UUIDType", "WaitUntilIdle", "WorkerFarmByForkType", "WorkerFarmsFamily", "ZmqAttach", "ZmqAttachedSocket", "ZmqBind", "ZmqClose", "ZmqConnect", "ZmqDealerSocket", "ZmqGetIdentity", "ZmqGetReceiveBufferSize", "ZmqGetReceiveCapacity", "ZmqGetSendBufferSize", "ZmqGetSendCapacity", "ZmqHasMore", "ZmqIsBound", "ZmqIsConnected", "ZmqIsOpen", "ZmqPoll", "ZmqPublisherSocket", "ZmqPullSocket", "ZmqPushSocket", "ZmqReceive", "ZmqReceiveList", "ZmqReceiveListAsString", "ZmqReplySocket", "ZmqRequestSocket", "ZmqRouterSocket", "ZmqSend", "ZmqSetIdentity", "ZmqSetReceiveBufferSize", "ZmqSetReceiveCapacity", "ZmqSetSendBufferSize", "ZmqSetSendCapacity", "ZmqSocket", "ZmqSocketType", "ZmqSocketURI", "ZmqSubscribe", "ZmqSubscriberSocket", "ZmqUnsubscribe", "_BUFFER", "_GapToJsonStreamInternal", "_JSON_Globals", "_JSON_addRef", "_JSON_clearRefs", "_KERNEL", "a", "b" ]
Variables in gap
have no type, the characteristics of the object they refer to are stored in the object itself. In fact, we can reuse the same variable to denote different types of data.
The last
identifier is used to refer to the last output (last2
and last3
are also available).
In gap
there is a convention of writing global variables and variables used in libraries starting with capital letters.
Functions are also objects, and can be defined in various ways. We can use a classical procedure.
And we can also use \(\lambda\)-calculus-style notation.
Lists in gap
are written, as we have already seen in some examples above, as a sequence of elements delimited by square brackets. Such sequences need not be homogeneous. When working with lists we have to pay attention to some functions that are ‘destructive’, that is, they modify some of the arguments passed to them. This is partly because when you assign an identifier to a list, you do not assign it to the object as such, but to its position in memory.
As we have seen in this example list[n]
accesses the n
th element of the list (starting at 1; in other languages the first position is the one corresponding to index 0).
We can use the ShallowCopy
command to make a copy of a list, creating a new object.
There are many ways to define and modify lists, some examples are given below.
To append to a
the elements of another list, we can use Append
:
If we want to add an element at the end of our list, we use Add
.
Concatenation
returns a new list from two given lists, concatenating them without modifying them.
Filtered` can be used to filter the elements of a list that meet a given condition.
[ 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97 ]
We can also apply a function to all the elements of a list.
[ 4, 9, 25, 49, 121, 169, 289, 361, 529, 841, 961, 1369, 1681, 1849, 2209, 2809, 3481, 3721, 4489, 5041, 5329, 6241, 6889, 7921, 9409 ]
Sometimes it is convenient to use sets instead of lists, as their elements are sorted (if they are comparable to each other) and the membership of an element is quicker to resolve.
There are certain commands whose output is a set.
The use of ..
can be used in a variety of ways, and captures the idea of range.
[ 1, 4, 9, 16, 25, 36, 49, 64, 81, 100, 121, 144, 169, 196, 225, 256, 289, 324, 361, 400 ]
In gap
we can create records with custom fields using rec
.
We can add new fields, or modify existing ones, simply by assigning them a value.
To access a field value, we use .
.
We can see the fields defined with RecNames
.
Or see whether a field is assigned or not
As with lists, if you want to make a copy, it is advisable to use ShallowCopy
.
The problem here is that r
itself contains another record. To avoid this, we use StructuralCopy
.
In gap
you can use commands like for
, while
, repeat
… until
to make loops.
Let us see how to define the factorial in different ways, using different implementation styles.
f:=function(x)
local p, i; #variable local para el producto parcial y contador
p:=1;
for i in [1..x] do
p:=p*i;
od; #final de bucle
return p; #salida
end;
function( x ) ... end
Recursively…
93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000
Using list-specific functions and \(\lambda\)-expressions …
57133839564458545904789328652610540031895535786011264182548375833179829124845398393126574488675311145377107878746854204162666250198684504466355949195922066574942592095735778929325357290444962472405416790722118445437122269675520000000000000000000000000000000000000
We now calculate the first perfect integer, and the perfect integers between 1 and 100000. For this we will use First
.
Let us see if all the numbers between 100 and 500 are perfect, or if there are any.
We do something similar with pairs of friendly numbers.
son_amigos:=function(x,y)
local dx, dy; #divisores de x e y
if x=y or IsPrime(x) then
return false;
fi; #queremos buscar parejas distintas y que no sean primos
dx:=List(DivisorsInt(x));
Remove(dx);; #le quitamos x
dy:=List(DivisorsInt(y));
Remove(dy);; #le quitamos y
return (Sum(dx)=y) and (x=Sum(dy));
end;
function( x, y ) ... end
To see which numbers between 1 and 50 have friends, we write, for example, the following.
If we want to search in a larger list, we can use iterators.